//
// Created by kode.zhong on 2021/3/6.
//

#ifndef LOX_VM_LOX_DEBUG_H_
#define LOX_VM_LOX_DEBUG_H_

#include <string>
#include <iostream>
#include <iomanip>

#include "chunk.h"

namespace lox {

void FormatHex(uint32_t value);
void FormatDecimal(uint32_t value);

template<typename VALUE_TYPE>
void FormatLine(uint32_t offset, const Chunk<VALUE_TYPE> &chunk) {
  if (offset > 0 && chunk.GetLines()[offset] == chunk.GetLines()[offset - 1]) {
    // equal to previous
    std::cout << "----" << " : ";
  } else {
    FormatDecimal(chunk.GetLines()[offset]);
  }
}

/**
 * Corresponding to SimpleInstruction
 */
template<typename VALUE_TYPE>
size_t UnaryInstruction(uint32_t offset, std::string_view op, const Chunk<VALUE_TYPE> &chunk) {
  FormatHex(offset);
  FormatLine(offset, chunk);
  std::cout << op << std::endl;
  return offset + 1;
}

template<typename VALUE_TYPE>
size_t ConstantInstruction(size_t offset, std::string_view op, const Chunk<VALUE_TYPE> &chunk) {
  size_t constant_idx = chunk.m_code[offset + 1];
  FormatHex(offset);
  FormatLine(offset, chunk);
  std::cout << op;
  std::cout << "(" << chunk.GetValue(constant_idx) << ")" << std::endl;
  return offset + 2;
}

template<typename VALUE_TYPE>
size_t DisassembleInstruction(const Chunk<VALUE_TYPE> &chunk, size_t offset) {
  switch (chunk.m_code[offset]) {
    case OP_CONSTANTS:return ConstantInstruction(offset, "OP_CONSTANTS", chunk);
    case OP_RETURN:return UnaryInstruction(offset, "OP_RETURN", chunk);
    case OP_NEGATE:return UnaryInstruction(offset, "OP_NEGATE", chunk);
    case OP_ADD: return UnaryInstruction(offset, "OP_ADD", chunk);
    case OP_SUBTRACT: return UnaryInstruction(offset, "OP_SUB", chunk);
    case OP_MULTIPLY: return UnaryInstruction(offset, "OP_MUL", chunk);
    case OP_DIVIDE: return UnaryInstruction(offset, "OP_DIV", chunk);
    default: {
      std::cout << "Illegal opcode." << std::endl;
      return offset + 1;
    }
  }
}

template<typename VALUE_TYPE>
void DisassembleChunk(const Chunk<VALUE_TYPE> &chunk, const std::string &name) {
  std::cout << "====" << name << "===" << std::endl;
  for (size_t offset = 0; offset < chunk.m_code.size();) {
    offset = DisassembleInstruction(chunk, offset);
  }
}

}

#endif //LOX_VM_LOX_DEBUG_H_
